/**
 * @file   ModForm.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Sat Sep 29 08:25:48 2007
 * 
 * @brief  
 * 
 * 
 */

package sim.forms;

import sim.framework.*;
import sim.framework.io.*;
import sim.framework.dialog.*;
import sim.utils.parser.*;
import sim.utils.Utils;
import sim.framework.dialog.DlgForm;
import java.util.Vector;
import java.util.Enumeration;

public abstract class ModForm extends Module implements FileGets {

    private static final String TITLE_LABEL = null;
    private static final String BNEXT_LABEL = "";
    private static final String BBACK_LABEL = "";
    private static final String BQUIT_LABEL = "";

    private static final int FILEGETS_LOADFORM = 1;
    private static final int FILEGETS_INITNODE = 2;

    // Formulario utilizado
    private Form m_Form = null;

    private Vector m_Stack = new Vector(4);

    // Nodo actualmente utilizado.
    protected FrmNode m_CurNode;

    public ModForm(int id, Simlet s) {
	super(id, s);
    }

    public void work(final Msg msg) {
	switch(msg.getWParam()) {
	case Executable.STATE_INIT:
	    // Si el form no ha sido proporsionado por la clase base ...
	    if (m_Form == null) {
		// Solicitarlo
		m_Form = formCreate();
	    }
	    // Si existe el formulario
	    if (m_Form != null) {
		// ... mostramos el primer nodo
		m_CurNode = null;
		commandNext();
	    }
	    else {
		// ... en caso contrario ignoramos.
		execNext();
	    }
	}
    }

    public boolean event(final Msg msg) {
	boolean ret = false;

	switch (msg.getEventCode()) {
	case EventListener.COMMAND:
	    {
		switch (msg.getWParam()) {
		case Dialog.CTRL_BUTTON_NEXT:
		    commandNext();
		    break;
		case Dialog.CTRL_BUTTON_BACK:
		    commandBack();
		    break;
		case Dialog.CTRL_BUTTON_EXIT:
		    commandExit();
		    ret = true;
		    break;
		}
	    }
	}

        return ret;
    }

    private void commandNext() {
	FrmNode node;
	DlgForm dlg;

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::commandNext() ENTERED");
	//#endif

	// Actualizar el nodo con la data del dialogo.
	updateDialog((DlgForm)getGui(), m_CurNode, true);

	// Buscar el siguiente nodo.
	node = nextNode();
	if (node != null) {
	    dlg = formCreateDlg(node);
	    if (dlg != null) {
		// Actulizar el dialogo con data del nodo.
		updateDialog(dlg, node, false);
		show(dlg);
		//#ifdef DEBUG_MODFORM
		//# System.out.println("ModForm::commandNext() EXITING");
		//#endif
		return;
	    }
	}

	//#ifdef DEBUG_MODFORM
	//# System.out.println("\tNo hay hay mas nodos hacia adelante.");
	//#endif

	formGoToNext();

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::commandNext() EXITING");
	//#endif
    }

    private FrmNode nextNode() {
	int nodeID;

	m_CurNode = m_Form.nextNode(m_CurNode);
	
	// Inicializar el nodo.
	if (m_CurNode != null) {
	    // Añadir a la cola
	    m_Stack.addElement(m_CurNode);
	    // Cargar su Items.
	    formInitItems(m_CurNode);
	}

	return m_CurNode;
    }

    private void commandBack() {
	FrmNode node;
	DlgForm dlg;

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::commandBack() ENTERED");
	//#endif

	// Actualizar el nodo con la data del dialogo.
	updateDialog((DlgForm)getGui(), m_CurNode, true);

	// Buscar el nodo anterior.
	node = backNode();
	if (node != null) {
	    dlg = formCreateDlg(node);
	    if (dlg != null) {
		// Actulizar el dialogo con data del nodo.
		updateDialog(dlg, node, false);
		show(dlg);
		return;
	    }
	}

	//#ifdef DEBUG_MODFORM
	//# System.out.println("\tNo hay hay mas nodos hacia atrás.");
	//#endif

	formGoToBack();

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::commandBack() EXITING");
	//#endif
    }

    private FrmNode backNode() {
	m_CurNode = null;
	if (m_Stack.size() > 0) {
	    // Remover el nodo actual de la cola.
	    m_Stack.removeElement((FrmNode)m_Stack.lastElement());
	    // Apuntar al anterior nodo de la cola.
	    if (m_Stack.size() > 0) {
		m_CurNode = (FrmNode)m_Stack.lastElement();
	    }
	}
	return m_CurNode;
    }


    private void commandExit() {
	formGoToExit();
    }

    private DlgForm formCreateDlg(final FrmNode node) {
	
	DlgForm dlg;
	
	dlg = (DlgForm)DlgForm.create(m_Simlet, 
				      node.m_Type,
				      formTitleLabel(node),
				      node.m_Info, 
				      node.m_Seek, 
				      formBNextLabel(node), 
				      formBBackLabel(node), 
				      formBQuitLabel(node));    
	return dlg;
    }

    private void show(DlgForm dlg) {
	setGui(dlg);
    }
    
    private void updateDialog(DlgForm dlg, final FrmNode node, boolean update) {
	if ((node != null) && (dlg != null)) {
	    node.updateDialog(dlg, update);
	}
    }

    /**
     * FUCIONES UTILIZADAS POR LOS CALLBACKS.
     * 
     */

    /** 
     * Devuelve los nodos de un formulario utilizados.
     * 
     * 
     * @return Devuelve una referencia a un vector con que contiene objetos FrmNode.
     */
    protected final Vector getOptions() {
	return m_Stack;
    }

    /** 
     * Devuelve los valores ingresados en los nodos utilizados del formulario.
     * 
     * @param strbuff Destino de los valores de cada nodo.
     * @param token Utilizado para serparar el valor de cada nodo.
     */
    protected final void getOptions(StringBuffer strbuff, final String token) {
	Enumeration e;
	FrmNode node;
	e = m_Stack.elements();
	while(e.hasMoreElements()) {
	    node = (FrmNode)e.nextElement();
	    // Añadir token si ya se añadió un node
	    if (strbuff.length() > 0) {
		strbuff.append(token);
	    }
	    node.getOption(strbuff);
	}
    }

    /** 
     * Devuelve los valores ingresados en los nodos utilizados del formulario.
     * 
     * @param strbuff Destino de los valores de cada nodo.
     */
    protected final void getOptions(StringBuffer strbuff) {
	getOptions(strbuff, FrmNode.STR_TOKEN);
    }

    /** 
     * Devuelve los valores ingresados en los nodos utilizados del formulario.
     * 
     * @param strbuff Destino de los valores de cada nodo.
     * @param token Arreglo de bytes que contiene el token
     * @param start Primer byte válido del arreglo token.
     * @param count Cantidad de bytes válidos de token a partir de start.
     */
    protected final void getOptions(StringBuffer strbuff, final byte[] token, int start, int count) {
	String strtok;
	strtok = decoding(token, start, count);
	getOptions(strbuff, strtok);
    }

    /** 
     * Devuelve el valor asociado a un nodo utilizado.
     *
     * La clase que extiende ModForm, puede utilizar esta función para recuperar
     * el dato ingresado en el control del dialogo asociado al nodo.
     * 
     * @param nodeId Identificador del nodo.
     * 
     * @return Devuelve una cadena de caracteres que representa en contenido
     *         ingresado en el control asociado.
     */
    protected final String getOption(int nodeId) {
	Enumeration e;
	FrmNode node;
	e = m_Stack.elements();
	while(e.hasMoreElements()) {
	    node = (FrmNode)e.nextElement();
	    // Añadir token si ya se añadió un node
	    if (node.m_Node == nodeId) {
		return node.getOption();
	    }

	}
	return null;
    }

    /**
     * LA SIGUIENTE FUNCIÓN DEBE SER ABSTRACTA
     * 
     */

    protected abstract Form formCreate();
    
    /** 
     * Obtiene el título para cada diálogo.
     *
     * Esta función es llamada por la función ModForm::createDlg() para obtener
     * la cadena de caracteres que mostrará como título para cada dialogo. 
     * 
     * @return Devuelve una cadena de carateres con la etiqueda para el titulo
     *         del dialogo. Si devuelve null (valor por defecto) el título no
     *         será mostrado.
     */
    protected String formTitleLabel(final FrmNode node) {
	return ModForm.TITLE_LABEL;
    }

    /** 
     * Obtiene la etiqueta del boton Next.
     *
     * Esta función es llamada por la función ModForm::createDlg() para obtener
     * la cadena de caracteres que mostrará la etiqueta asociada al botón Next.
     * 
     * @return Devuelve una cadena de carateres con la etiqueda del boton
     *         Next. Si devuelve null, el botón no será mostrado. Si vale
     *         una cadena vacía (es decir "", valor por defecto) mostrará
     *         un icono que representa la opción siguiente.
     */
    protected String formBNextLabel(final FrmNode node) {
	return ModForm.BNEXT_LABEL;
    }

    /** 
     * Obtiene la etiqueta del boton Back.
     *
     * Esta función es llamada por la función ModForm::createDlg() para obtener
     * la cadena de caracteres que mostrará la etiqueta asociada al botón Back.
     * 
     * @return Devuelve una cadena de carateres con la etiqueda del boton
     *         Back. Si devuelve null, el botón no será mostrado. Si vale
     *         una cadena vacía (es decir "", valor por defecto) mostrará
     *         un icono que representa la opción atrás.
     */
    protected String formBBackLabel(final FrmNode node) {
	return ModForm.BBACK_LABEL;
    }

    /** 
     * Obtiene la etiqueta del boton Quit.
     *
     * Esta función es llamada por la función ModForm::createDlg() para obtener
     * la cadena de caracteres que mostrará la etiqueta asociada al botón Quit.
     * 
     * @return Devuelve una cadena de carateres con la etiqueda del boton
     *         Quit. Si devuelve null, el botón no será mostrado. Si vale
     *         una cadena vacía (es decir "", valor por defecto) mostrará
     *         un icono que representa la opción salir.
     */
    protected String formBQuitLabel(final FrmNode node) {
	return ModForm.BQUIT_LABEL;
    }

    /** 
     * Inicializa los items de un nodo.
     *
     * Este método carga items de una archivo del tipo *.lst para un nodo. Este
     * método puede ser redefinido por la clase que extiende ModForm.
     * 
     * @param node Nodo para el cual se va a cargar los items.
     */
    protected void formInitItems(final FrmNode node) {
	FileStream file;
	
	// Si el nodo ya tiene Items, entonces esta inicializado.
	if (node.itemsCount() > 0) {
	    return;
	}

	// Cargar los items del archivo lista *.lst
	if (node.getFilename() != null) {
	    file = (FileStream)FileStream.create(m_Simlet, node.getFilename(), File.MODE_READ);
	    if (file != null) {
		file.gets(this);
		file.close();
	    }
 	}
    }

    public final boolean fileGetsLine(final byte[] line, int size, int crlf) {
	FrmItem newItem;

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::fileGetsLine() ENTERED");
	//#endif

	if (size > crlf) {
	    if (m_CurNode != null) {
		newItem = FrmItem.create(getAppEncoding(), m_CurNode,
					 line, 0, size - crlf );
		if (newItem != null) {
		    m_CurNode.appendItem(newItem);
		}
	    }
	}

	//#ifdef DEBUG_MODFORM
	//# System.out.println("ModForm::fileGetsLine() EXITING");
	//#endif

	return true;
    }

    /** 
     * Esta función es llamada por ModForm cuando el usuario presiona
     * el boton atrás en el cuadro de dialogo del primer nodo.
     * 
     * Por defecto, esta función llama a la función execBack(). La
     * clase que extiende ModForm, puede redefinirla para modificar
     * el comportamiento por defecto.
     */
    protected void formGoToBack() {
	execBack();
    }

    /** 
     * Esta función es llamada por ModForm cuando el usuario presiona
     * el boton siguiente en el cuadro de dialogo del último nodo.
     *
     * Por defecto, esta función llama a la función execNext(). La
     * clase que extiende ModForm, puede redefinirla para modificar
     * el comportamiento por defecto.
     */
    protected void formGoToNext() {
	execNext();
    }

    /** 
     * Esta función es llamada por ModForm cuando el usuario presiona
     * el boton salir en el cuadro de dialogo de cualquier nodo.
     * 
     * Por defecto, esta función llama a la función execExit(). La
     * clase que extiende ModForm, puede redefinirla para modificar
     * el comportamiento por defecto.
     */
    protected void formGoToExit() {
	execNext();
    }
}
